Popular Searches
Popular Course Categories
Popular Courses

Implementing Navigation Between Screens

Implementing Navigation Between Screens

User Interaction, Forms, and Navigation

Implementing Navigation Between Screens in Jetpack Compose

Course: Android App Development

Topic: Implementing Navigation Between Screens

Course Link: Android App Development Course

Demo Link: Register for a Course Demo

Website: JustAcademy


1. Introduction to Navigation in Android

Navigation is the process of moving users from one screen to another within an application. Most modern Android applications contain multiple screens that perform different tasks. For example, a shopping application may contain a home screen, product listing screen, product details screen, cart screen, and checkout screen.

In Jetpack Compose, navigation allows developers to connect composable screens and control which screen appears at a particular moment. Instead of manually replacing UI content and managing every back-button action, developers can use the Android Jetpack Navigation component.

For Compose-based applications, the Navigation Compose library provides components such as NavController, NavHost, and composable() to define and manage screen transitions.

A good navigation implementation helps users understand where they are, move between related screens, return to previous screens, and share information between destinations without making the application difficult to maintain.

2. Learning Objectives

After studying this topic, you should be able to:

  • Understand navigation and destinations in Android applications.
  • Explain the purpose of NavController, NavHost, and navigation routes.
  • Add the Navigation Compose dependency to an Android project.
  • Create a navigation graph with multiple composable destinations.
  • Navigate between screens using buttons and callbacks.
  • Understand the navigation back stack and Back button behavior.
  • Pass data between screens using route arguments.
  • Prevent unnecessary duplicate destinations.
  • Implement navigation with top app bars and back buttons.
  • Understand nested navigation, bottom navigation, and deep links.
  • Apply navigation architecture and testing best practices.

3. What Is Navigation?

Navigation connects the different screens or destinations in an application. Each destination represents a particular part of the user interface, such as a login page, profile page, settings page, or product details page.

For example, a student application may provide the following flow:

Login Screen
     |
     v
Home Screen
     |
     v
Course List Screen
     |
     v
Course Details Screen
     |
     v
Enrollment Screen

Each arrow represents a navigation action. When the user selects a course from the course list, the application opens the corresponding details screen. When the user presses Back, the application can return to the course list.

Why is navigation important?

  • Better user experience: Users can move through the application in a predictable way.
  • Clear screen organization: Each destination can focus on a specific task.
  • Back-stack management: Previous destinations can be restored through navigation history.
  • Data transfer: Screen arguments can identify the content to display.
  • Maintainability: A navigation graph centralizes the relationships between destinations.
  • Deep linking: Supported links can open a particular destination directly.

4. Understanding Navigation Compose

Navigation Compose is the Navigation component integration for applications whose UI is built using Jetpack Compose. It allows composable functions to act as destinations in a navigation graph.

Three important concepts are the navigation controller, navigation graph, and navigation host.

Component Purpose Example
NavController Controls navigation and manages the back stack. Calling navigate() to open a profile screen.
NavHost Displays the destination selected by the navigation graph. Showing the current home, profile, or settings screen.
NavGraph Defines the destinations and their navigation structure. Connecting Home to Course List and Course Details.
Route Identifies a destination and may represent its required arguments. "home" or "course/12".
Destination A screen or other navigable content in the graph. Course Details.
Back stack Tracks the navigation entries used to determine Back behavior. Returning from Details to Course List.

5. Adding the Navigation Compose Dependency

To use Navigation Compose, add its dependency to the app module's Gradle file. Use a version compatible with your project's Android Gradle Plugin, Kotlin version, and Compose setup.

For a Gradle Kotlin DSL file named app/build.gradle.kts, the dependency entry has this form:

dependencies {
    implementation("androidx.navigation:navigation-compose:<compatible-version>")
}

Replace <compatible-version> with the appropriate released version from the official Android documentation or your project's version catalog. The placeholder is not a literal version and should not be copied unchanged into a production build.

After adding the dependency, synchronize the Gradle project. If Android Studio reports unresolved navigation imports, check that the dependency is declared in the app module and that Gradle synchronization has completed successfully.

6. Creating Navigation Routes

A route identifies a destination in the navigation graph. In a simple application, routes can be represented by unique strings. For example, "home", "profile", and "settings" can represent three different screens.

Using constants helps prevent spelling mistakes and avoids repeating route strings throughout the application.

object AppRoutes {
    const val HOME = "home"
    const val PROFILE = "profile"
    const val SETTINGS = "settings"
}

These constants can be used in both the navigation graph and navigation calls.

Why should routes be unique?

  • Each destination needs a reliable identifier.
  • Incorrect route strings can lead to navigation errors.
  • Centralized route definitions are easier to maintain.
  • Route arguments must follow a consistent format.

Modern Navigation versions also support type-safe routes based on serializable Kotlin objects and classes. String routes are used in the introductory examples below because they make the underlying navigation concepts straightforward to understand.

7. Creating a NavController

The NavController coordinates navigation between destinations. In Compose, create and remember a controller using rememberNavController().

import androidx.compose.runtime.Composable
import androidx.navigation.compose.rememberNavController

@Composable
fun AppNavigation() {
    val navController = rememberNavController()

    // Define the NavHost here.
}

The controller should normally be created high enough in the UI hierarchy that the navigation graph can use it and screens can report navigation events through callbacks.

Remembering the controller allows Navigation Compose to maintain its state appropriately across recompositions and supported configuration changes.

8. Creating a NavHost

A NavHost is the container that displays the destination selected by the navigation controller. It is configured with a controller, a starting destination, and a navigation graph.

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController

@Composable
fun AppNavigation() {
    val navController = rememberNavController()

    NavHost(
        navController = navController,
        startDestination = AppRoutes.HOME,
        modifier = Modifier.fillMaxSize()
    ) {
        composable(route = AppRoutes.HOME) {
            HomeScreen()
        }

        composable(route = AppRoutes.PROFILE) {
            ProfileScreen()
        }

        composable(route = AppRoutes.SETTINGS) {
            SettingsScreen()
        }
    }
}

This example assumes that the three screen composables and the AppRoutes object are defined in the project. The startDestination specifies the screen shown when the navigation host starts.

Understanding the code

  1. rememberNavController() creates the navigation controller.
  2. NavHost() connects the controller to the navigation graph.
  3. startDestination specifies the initial route.
  4. composable() declares a destination in the graph.
  5. The lambda passed to composable() builds the UI for that destination.

9. Creating the First Screen

The first screen can display a heading and a button that takes the user to the profile screen. The screen should report the user's action through a callback rather than directly controlling the navigation controller.

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier

@Composable
fun HomeScreen(
    onOpenProfile: () -> Unit
) {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            text = "Home Screen",
            style = MaterialTheme.typography.headlineMedium
        )

        Button(
            onClick = onOpenProfile
        ) {
            Text("Open Profile")
        }
    }
}

The callback onOpenProfile represents the action that should happen when the user presses the button. The screen does not need to know which route is used or how the navigation controller is configured.

10. Connecting the Screen to Navigation

Connect the Home screen callback to the navigation controller inside the navigation graph. When the user presses the button, the controller navigates to the Profile destination.

@Composable
fun AppNavigation() {
    val navController = rememberNavController()

    NavHost(
        navController = navController,
        startDestination = AppRoutes.HOME
    ) {
        composable(AppRoutes.HOME) {
            HomeScreen(
                onOpenProfile = {
                    navController.navigate(AppRoutes.PROFILE)
                }
            )
        }

        composable(AppRoutes.PROFILE) {
            ProfileScreen(
                onBack = {
                    navController.popBackStack()
                }
            )
        }
    }
}

This example demonstrates the recommended separation of responsibilities: the screen emits an event, and the navigation graph decides what navigation action to perform.

11. Building a Complete Two-Screen Example

The following example demonstrates navigation between a Home screen and a Profile screen. It includes a navigation graph, a button to open the Profile screen, and a Back button that returns to the previous destination.

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController

object AppRoutes {
    const val HOME = "home"
    const val PROFILE = "profile"
}

@Composable
fun AppNavigation() {
    val navController = rememberNavController()

    NavHost(
        navController = navController,
        startDestination = AppRoutes.HOME,
        modifier = Modifier.fillMaxSize()
    ) {
        composable(AppRoutes.HOME) {
            HomeScreen(
                onOpenProfile = {
                    navController.navigate(AppRoutes.PROFILE)
                }
            )
        }

        composable(AppRoutes.PROFILE) {
            ProfileScreen(
                onBack = {
                    navController.popBackStack()
                }
            )
        }
    }
}

@Composable
fun HomeScreen(
    onOpenProfile: () -> Unit
) {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            text = "Welcome Home",
            style = MaterialTheme.typography.headlineMedium
        )

        Button(onClick = onOpenProfile) {
            Text("Open Profile")
        }
    }
}

@Composable
fun ProfileScreen(
    onBack: () -> Unit
) {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            text = "Profile Screen",
            style = MaterialTheme.typography.headlineMedium
        )

        Button(onClick = onBack) {
            Text("Back to Home")
        }
    }
}

Call AppNavigation() from your activity's Compose content, typically within the app's Material theme. This example uses a simple layout for learning purposes; production screens can use more detailed UI components.

12. Understanding the Navigation Back Stack

The back stack stores the history of navigation entries. When the user navigates from Home to Profile, the Profile destination becomes the current destination and Home generally remains below it in the stack.

Initial state:
[Home]

After navigating to Profile:
[Home, Profile]

After pressing Back:
[Home]

When a user presses Back, Navigation normally removes the current entry and returns to the previous destination if one exists. The operating system's Back behavior and the app's navigation graph work together to provide a predictable experience.

Important back-stack methods

Method Purpose Typical Use
navigate(route) Navigates to a destination. Opening a profile or details screen.
popBackStack() Removes the current entry and returns to a previous entry when possible. Returning from Details to List.
popUpTo() Removes entries up to a specified destination according to the configured options. Clearing part of the stack after login.
launchSingleTop Prevents adding another copy at the top when the same destination is already there. Avoiding duplicate top destinations.
restoreState Requests restoration of saved destination state when supported by the navigation options. Restoring state during navigation patterns such as bottom navigation.

13. Returning to a Previous Screen with popBackStack()

Use popBackStack() when the user should return to a previous destination. It is commonly used for a Back button in a top app bar or a custom navigation control.

Button(
    onClick = {
        navController.popBackStack()
    }
) {
    Text("Go Back")
}

Use this method only when there is a previous destination to return to. If the back stack is empty or the requested pop cannot be performed, the operation may return false. Applications should define appropriate behavior for cases in which no previous destination exists.

14. Passing Data Between Screens

Many screens need information from the destination that opened them. For example, a product details screen needs the selected product ID, and a profile screen may need a user ID.

For simple string-based routes, route arguments can be included in the route definition. A destination can then read the argument through its NavBackStackEntry.

Example: passing a user ID

object UserRoutes {
    const val USERS = "users"
    const val USER_DETAILS = "user_details/{userId}"

    fun userDetails(userId: String): String {
        return "user_details/$userId"
    }
}

The details route contains a placeholder named userId. When navigating, provide a real value in place of the placeholder.

navController.navigate(
    UserRoutes.userDetails("42")
)

Defining the destination with an argument

import androidx.navigation.NavType
import androidx.navigation.navArgument
import androidx.navigation.compose.composable

composable(
    route = UserRoutes.USER_DETAILS,
    arguments = listOf(
        navArgument("userId") {
            type = NavType.StringType
        }
    )
) { backStackEntry ->
    val userId = backStackEntry.arguments
        ?.getString("userId")
        .orEmpty()

    UserDetailsScreen(
        userId = userId,
        onBack = {
            navController.popBackStack()
        }
    )
}

This example uses a string argument. Use an appropriate navigation argument type for other primitive values. For optional or complex data, consider the official Navigation guidance for your library version. Avoid placing sensitive information such as passwords, access tokens, or private authentication secrets in routes.

15. Creating a Details Screen

The details screen receives the identifier passed through navigation. It can use that identifier to display information from a repository or ViewModel.

@Composable
fun UserDetailsScreen(
    userId: String,
    onBack: () -> Unit
) {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            text = "User Details",
            style = MaterialTheme.typography.headlineMedium
        )

        Text(text = "Selected user ID: $userId")

        Button(onClick = onBack) {
            Text("Back")
        }
    }
}

In a real application, the screen should usually load the selected user's actual data using the identifier. The example only displays the ID to demonstrate the navigation argument.

16. Avoiding Duplicate Destinations

Repeated button taps or navigation events may create duplicate entries in the back stack. Whether this is a problem depends on the screen and the application's navigation design.

For cases where a destination should not be added again when it is already at the top, use launchSingleTop.

navController.navigate(AppRoutes.HOME) {
    launchSingleTop = true
}

This option prevents a second copy of the same destination from being added on top of itself. It does not automatically remove every older instance of that destination elsewhere in the stack.

Using popUpTo()

The popUpTo() option can remove destinations from the back stack up to a specified route.

navController.navigate(AppRoutes.HOME) {
    popUpTo(AppRoutes.HOME) {
        inclusive = false
    }
    launchSingleTop = true
}

Use the correct route and inclusive setting for your intended navigation flow. The sample demonstrates the option's structure; back-stack behavior should be tested against the graph and current destination in your actual application.

17. Implementing a Top App Bar with Back Navigation

A top app bar can display the current screen title and a Back button. Material 3 provides TopAppBar and related app-bar components.

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DetailsTopBar(
    onBack: () -> Unit
) {
    TopAppBar(
        title = {
            Text("Details")
        },
        navigationIcon = {
            IconButton(onClick = onBack) {
                Icon(
                    imageVector =
                        Icons.AutoMirrored.Filled.ArrowBack,
                    contentDescription = "Navigate back"
                )
            }
        }
    )
}

This snippet requires the corresponding Material 3 and icon imports. Depending on the Material 3 version, the app-bar API may require the experimental annotation shown above or may be stable. Use the API status that matches the version in your project.

18. Using Scaffold with Navigation

Scaffold provides a standard layout structure for Material screens. It can organize a top app bar, bottom bar, floating action button, and the content area where a navigation host is displayed.

import androidx.compose.material3.Scaffold
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.foundation.layout.padding
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppScaffold() {
    val navController = rememberNavController()

    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    Text("My Application")
                }
            )
        }
    ) { innerPadding ->
        NavHost(
            navController = navController,
            startDestination = AppRoutes.HOME,
            modifier = Modifier.padding(innerPadding)
        ) {
            composable(AppRoutes.HOME) {
                HomeScreen(
                    onOpenProfile = {
                        navController.navigate(
                            AppRoutes.PROFILE
                        )
                    }
                )
            }

            composable(AppRoutes.PROFILE) {
                ProfileScreen(
                    onBack = {
                        navController.popBackStack()
                    }
                )
            }
        }
    }
}

The padding supplied by Scaffold should be applied to the content area to prevent the navigation host from drawing underneath the app bar or other scaffold components. This example reuses the previously defined route and screen functions.

19. Bottom Navigation Between Main Sections

Many applications have several top-level sections, such as Home, Search, Favorites, and Profile. A bottom navigation bar can provide quick access to these sections.

For a simple implementation, a Material 3 NavigationBar can be placed in the bottomBar slot of a Scaffold. Each item should navigate to the corresponding top-level destination.

import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.navigation.compose.currentBackStackEntryAsState

@Composable
fun BottomNavigationExample() {
    val navController = rememberNavController()
    val navItems = listOf(
        AppRoutes.HOME,
        AppRoutes.PROFILE,
        AppRoutes.SETTINGS
    )

    val backStackEntry by
        navController.currentBackStackEntryAsState()
    val currentRoute =
        backStackEntry?.destination?.route

    Scaffold(
        bottomBar = {
            NavigationBar {
                navItems.forEach { route ->
                    NavigationBarItem(
                        selected = currentRoute == route,
                        onClick = {
                            navController.navigate(route) {
                                launchSingleTop = true
                            }
                        },
                        icon = {},
                        label = {
                            Text(route.replaceFirstChar {
                                it.uppercase()
                            })
                        }
                    )
                }
            }
        }
    ) { innerPadding ->
        NavHost(
            navController = navController,
            startDestination = AppRoutes.HOME,
            modifier = Modifier.padding(innerPadding)
        ) {
            composable(AppRoutes.HOME) {
                HomeScreen(
                    onOpenProfile = {
                        navController.navigate(
                            AppRoutes.PROFILE
                        )
                    }
                )
            }

            composable(AppRoutes.PROFILE) {
                ProfileScreen(
                    onBack = {
                        navController.popBackStack()
                    }
                )
            }

            composable(AppRoutes.SETTINGS) {
                SettingsScreen()
            }
        }
    }
}

This is an introductory example. For a polished production bottom navigation experience, provide meaningful icons, configure navigation options such as popUpTo() and state restoration where appropriate, and define a deliberate top-level back-stack strategy. The selected item should accurately reflect the current destination, including any nested destinations belonging to a top-level section.

20. Understanding Nested Navigation

Nested navigation organizes related destinations into separate navigation graphs. For example, an application might have an authentication graph containing Login and Registration screens and a main graph containing Home, Profile, and Settings.

Nested graphs help organize larger applications, keep related navigation logic together, and define logical navigation boundaries.

Application
  |
  +-- Authentication Graph
  |      +-- Login
  |      +-- Registration
  |
  +-- Main Graph
         +-- Home
         +-- Profile
         +-- Settings

When designing nested graphs, decide how navigation should behave when the user logs in, logs out, or presses Back. For example, after successful authentication, an app may remove the login destination from the back stack so that pressing Back does not return to the login screen.

21. Deep Links in Navigation

A deep link allows an application to open a particular destination from a URL, notification, or another application. For example, tapping a course link might open the course details screen directly rather than forcing the user to navigate through several screens.

Navigation Compose supports deep links through destination configuration. A deep link usually includes a URI pattern that identifies the destination and any required parameters.

import android.net.Uri
import androidx.navigation.navDeepLink

composable(
    route = "course/{courseId}",
    deepLinks = listOf(
        navDeepLink {
            uriPattern =
                "https://example.com/course/{courseId}"
        }
    )
) { backStackEntry ->
    val courseId = backStackEntry.arguments
        ?.getString("courseId")
        .orEmpty()

    CourseDetailsScreen(courseId = courseId)
}

This snippet demonstrates a destination-level URI pattern. For a deep link to launch the app from outside the application, configure the relevant manifest intent filters and any required domain verification. Use a real domain controlled by your application instead of the example domain.

Always validate data received from deep links. A route parameter should not be treated as proof that the user is authorized to view the requested information.

22. Navigation and State Management

Navigation and UI state are related but serve different purposes. Navigation determines which destination is active, while UI state stores information needed to render that destination, such as a selected tab, search query, form values, or loaded records.

  • Use navigation routes to identify destinations and pass small identifying arguments.
  • Use a ViewModel or another suitable state holder for screen state that needs to survive recomposition and configuration changes.
  • Use a repository for data access when the application needs to retrieve or persist information.
  • Avoid putting large domain objects or sensitive data directly into route strings.
  • Consider saved-state mechanisms when appropriate for state restoration.

For example, a course details destination can receive a course ID and use a ViewModel to load the full course information. This keeps route data small and separates navigation from data retrieval.

23. Recommended Navigation Architecture

In a maintainable Compose application, navigation should generally be managed at a high level, while individual screen composables expose callbacks for user actions.

com.example.myapp
    navigation
        AppRoutes.kt
        AppNavigation.kt
    ui
        screens
            HomeScreen.kt
            ProfileScreen.kt
            SettingsScreen.kt
        components
            AppTopBar.kt
    viewmodel
        ProfileViewModel.kt
    data
        UserRepository.kt

Responsibilities

  • AppRoutes: Defines route identifiers or type-safe route objects.
  • AppNavigation: Creates the navigation controller and graph.
  • Screen composables: Render the UI and expose event callbacks.
  • ViewModels: Manage screen state and business-related UI logic.
  • Repositories: Provide data to the application from databases, network services, or other sources.

For small projects, some of these elements can be kept together. As the application grows, separating them makes navigation easier to test and maintain.

24. Common Navigation Errors and Troubleshooting

Problem Possible Cause Solution
Unresolved navigation imports The Navigation Compose dependency is missing or Gradle has not synchronized. Check the app module dependency and sync the project.
Navigation fails with an unknown route The route string does not match a registered destination. Use shared constants or matching type-safe routes.
Back button closes the app unexpectedly There is no previous entry in the back stack. Review the start destination and back-stack configuration.
Repeated taps create duplicate screens Navigation is triggered repeatedly without a suitable stack policy. Consider launchSingleTop where appropriate.
Screen argument is null The argument name or type is incorrect, or the route was constructed incorrectly. Check the route placeholder, argument declaration, and navigation call.
Top app bar covers the screen The scaffold's content padding is not applied. Apply the padding provided to the Scaffold content lambda.
Back returns to an unexpected destination The stack contains extra entries or was modified by navigation options. Inspect the navigation flow and test popUpTo() behavior.
Deep link does not open the expected screen The URI pattern, intent filter, or domain configuration is incorrect. Verify the destination deep link and manifest configuration.
Screen directly depends on NavController Navigation responsibilities are coupled to UI components. Expose callbacks and handle navigation at the graph level.

25. Navigation Best Practices

  • Use Navigation Compose for Compose-based screens: It provides a consistent way to manage destinations and back-stack behavior.
  • Keep routes consistent: Use constants or type-safe route definitions rather than scattered string literals.
  • Keep navigation at a suitable level: Pass callbacks to screens instead of exposing the entire navigation controller unnecessarily.
  • Use clear route arguments: Pass IDs and small, non-sensitive values when possible.
  • Define Back behavior intentionally: Test both app-bar Back buttons and system Back navigation.
  • Avoid unnecessary duplicate entries: Use navigation options that match the intended user flow.
  • Organize larger graphs: Use nested graphs when they make features easier to understand.
  • Handle authentication transitions carefully: Prevent unwanted returns to authentication screens after login when appropriate.
  • Validate deep-link inputs: Never rely on route arguments alone for authorization.
  • Test navigation: Cover successful transitions, invalid arguments, Back behavior, and deep links.

26. Practical Exercise: Create a Three-Screen Application

Create an Android application containing Home, Profile, and Settings screens.

Requirements

  1. Create route constants for all three screens.
  2. Set Home as the start destination.
  3. Add a button on Home that opens Profile.
  4. Add a button on Profile that opens Settings.
  5. Implement Back navigation from Settings to Profile.
  6. Implement Back navigation from Profile to Home.
  7. Display the current screen title on every destination.
  8. Test the app using both visible buttons and the system Back action.
  9. Check that repeated navigation does not create unexpected duplicate destinations.

Expected navigation flow

Home
  |
  | Tap "Open Profile"
  v
Profile
  |
  | Tap "Open Settings"
  v
Settings
  |
  | Press Back
  v
Profile
  |
  | Press Back
  v
Home

27. Mini Project: Course Catalog Application

Build a small course catalog application to practice navigation in a realistic scenario.

Project screens

  • Home Screen: Displays a welcome message and featured courses.
  • Course List Screen: Shows a scrollable list of available courses.
  • Course Details Screen: Displays the selected course's title, description, and duration.
  • Enrollment Screen: Shows a registration form for the selected course.

Project requirements

  1. Create a route for each destination.
  2. Use NavHost to define the navigation graph.
  3. Navigate from Home to Course List.
  4. Pass a course ID from the list to the details screen.
  5. Navigate from Details to Enrollment.
  6. Provide a Back action on screens where returning is appropriate.
  7. Display a loading state if course details are retrieved asynchronously.
  8. Validate user input on the enrollment form.
  9. Test Back behavior and route arguments.
  10. Keep navigation logic separate from the individual screen UI.

28. Interview Questions and Answers

Q1. What is navigation in Android?

Answer: Navigation is the process of moving between different screens or destinations in an application.

Q2. What is Navigation Compose?

Answer: Navigation Compose is the Jetpack Navigation integration for applications that use Jetpack Compose destinations.

Q3. What is NavController?

Answer: NavController controls navigation between destinations, maintains the back stack, and provides methods for navigating and returning to previous destinations.

Q4. What is NavHost?

Answer: NavHost is a composable container that displays the current destination defined in the navigation graph.

Q5. What is a navigation route?

Answer: A route identifies a destination and may include the arguments required by that destination.

Q6. What is a navigation graph?

Answer: A navigation graph defines the destinations in an application and the structure used to navigate between them.

Q7. What does popBackStack() do?

Answer: It removes the current navigation entry and returns to a previous entry when the stack allows it.

Q8. What is the navigation back stack?

Answer: It is the collection of navigation entries that determines the history of destinations and how Back navigation behaves.

Q9. How can data be passed between destinations?

Answer: Data can be passed through navigation arguments, including route parameters. For larger or shared data, a ViewModel or repository may be more appropriate.

Q10. What is launchSingleTop?

Answer: It prevents a new copy of a destination from being added when that destination is already at the top of the back stack.

Q11. What is a deep link?

Answer: A deep link allows a destination to be opened directly from a supported URI or another entry point, such as a notification.

Q12. Why should NavController not be passed to every screen?

Answer: Passing callbacks keeps screen composables more reusable and testable. It also separates UI events from navigation decisions.

Q13. What is nested navigation?

Answer: Nested navigation groups related destinations into smaller navigation graphs, helping organize larger applications.

Q14. How should navigation be handled after login?

Answer: Navigate to the authenticated part of the app and configure the back stack so that returning to the login screen is prevented when that matches the intended authentication flow.

29. Quick Revision Notes

  • Navigation connects screens and destinations in an Android application.
  • Navigation Compose supports composable destinations.
  • NavController manages navigation operations and the back stack.
  • NavHost displays the current destination.
  • composable() defines a destination in a string-based navigation graph.
  • The start destination is the initial screen of the navigation host.
  • navigate() opens another destination.
  • popBackStack() returns to a previous destination when possible.
  • popUpTo() configures which entries are removed from the back stack.
  • launchSingleTop prevents another copy at the top of the stack.
  • Navigation arguments can pass small identifiers between screens.
  • Deep links can open a destination from a supported URI.
  • Passing callbacks to screen composables helps separate UI and navigation logic.
  • Always test Back behavior, route arguments, and duplicate-navigation scenarios.

30. Conclusion

Implementing navigation between screens is an essential part of modern Android development. Jetpack Compose Navigation provides a structured approach to connecting destinations, managing the back stack, passing route arguments, and supporting navigation patterns such as top app bars, bottom navigation, and deep links.

By learning how to configure NavController, NavHost, routes, and navigation callbacks, developers can create applications that are easier to navigate, maintain, and test. For larger applications, combining navigation with clear state management, ViewModels, and repositories helps keep the architecture organized.

31. Additional Learning Resources

Previous Next
whatsapp